﻿using System;
namespace ASquared.ModelOptimization
{
    public class DividedDifference
    {
        /// <summary>Calculates the divided differences for a list of 'x' values and function values or function definition.</summary>
        /// <param name="xvals">The list of 'x' values.</param>
        /// <param name="yvals">The function evaluated at each 'x' value.</param>
        /// <returns>Returns the divided difference f[x0,...,xn].</returns>
        public static Double Calculate(Double[] xvals, Double[] yvals)
        {
            Int32 len = yvals.Length;
            if (len != xvals.Length) throw new Exception("In order to calculate divided differences, the xvals and yvals arrays must be the same length.");
            if (len == 0) throw new Exception("There needs to be some data within the arrays.");
            if (len == 1) return yvals[0];
            
            // Declare sub arrays
            Double[] subX1 = new Double[len - 1],
                subX2 = new Double[len - 1],
                subY1 = new Double[len - 1],
                subY2 = new Double[len - 1];

            // Fill sub arrays 
            for (Int32 i = 0; i + 1 < len; i++)
            {
                subX1[i] = xvals[i];
                subY1[i] = yvals[i];

                subX2[i] = xvals[i + 1];
                subY2[i] = yvals[i + 1];
            }

            // Calculate the sub arrays.
            return (Calculate(subX2, subY2) - Calculate(subX1, subY1)) / (xvals[len - 1] - xvals[0]);
        }
        /// <summary>Calculates the divided differences for a list of 'x' values and function values or function definition.</summary>
        /// <param name="xvals">The list of 'x' values.</param>
        /// <param name="fxn">The function definition to evaluate each 'x' value.</param>
        /// <returns>Returns the divided difference f[x0,...,xn].</returns>
        public static Double Calculate(Double[] xvals, ISingleVariableFunction fxn)
        {
            return Calculate(xvals, Array.ConvertAll(xvals, element => fxn.GetValue(element)));
        }
    }
}
